﻿#if NETFX
using Color = System.Windows.Media.Color;
#elif NETFX_CORE || WINDOWS_PHONE
using Color = Windows.UI.Color;
#endif


using LibNiconico.NiconicoApi;
using LibNiconico.NiconicoApi.Internal;
using LibNiconico.NiconicoApi.Lives;
using LibNiconico.NiconicoApi.Lives.Comments;
using LibNiconico.NiconicoApi.Utils;
using NicoView.Model.Service.Lives;
using NicoView.Model.Service.Property;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;


namespace NicoView.Model.Service.Comments
{
    public enum CommentUserType
    {
        General,
        Premium,
        Owner,
        Manager
    }

    public enum CommentBodyType
    {
        Normal,
        CommentArt,
        ShowCommand,
        NoneShowCommand,
        ManageCommand,
    }

    public enum CommentScoreRank
    {
        None,
        LittleNg,
        MediumNg,
        ManyNg
    }

    public enum CommentType
    {
        None,
        Live,
        Video,
    }

    public static class CommentScoreRankExtention
    {
        public static string GetDescription(this CommentScoreRank scoreRank)
        {

            switch (scoreRank)
            {
                case CommentScoreRank.None:
                    return "NG共有されていないコメントか、ごく微量のNG設定";

                case CommentScoreRank.LittleNg:
                    return "NG共有されたコメント(まだセフセフ)";
                    
                case CommentScoreRank.MediumNg:
                    return "NG共有されたコメント(ちょいやば)";

                case CommentScoreRank.ManyNg:
                    return "NG共有されたコメント(完全にアウトー！)";

                default:
                    return "不明なスコア";

            }
        }


        /// <summary>
        /// 表示すべきではないNGランクか判定
        /// </summary>
        /// <param name="scoreRank"></param>
        /// <param name="ngShareLevel"></param>
        /// <returns></returns>
        public static bool IsNotShowNgRank(this CommentScoreRank scoreRank, CommentNgShareLevel ngShareLevel)
        {
            if (IsNgRank(scoreRank))
            {
                switch (ngShareLevel)
                {
                    case CommentNgShareLevel.Low:
                        return scoreRank == CommentScoreRank.ManyNg;

                    case CommentNgShareLevel.Medium:
                        return scoreRank == CommentScoreRank.ManyNg || scoreRank == CommentScoreRank.MediumNg;

                    case CommentNgShareLevel.High:
                        return true;
                        

                    default:
                        return false;

                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// NGランクか判定
        /// </summary>
        /// <param name="scoreRank"></param>
        /// <returns></returns>
        public static bool IsNgRank(this CommentScoreRank scoreRank)
        {
            return scoreRank == CommentScoreRank.LittleNg || scoreRank == CommentScoreRank.MediumNg || scoreRank == CommentScoreRank.ManyNg;

        }
    }

    public sealed class NotifyNicoComment:NicoViewObservableObject
    {
        internal NotificationNicoUser user;


        private bool isJapanese;



        private TimeSpan elapsed;

        private DateTime time;

        private int no;

        private CommentUserType userType;

        private CommentBodyType bodyType;

        private CommentScoreRank commentScoreRank;

        private bool isNew;

        public NotifyNicoComment()
        {
            this.user = null;
            this.isJapanese = true;
            this.no = 0;
            this.userType = CommentUserType.General;
            this.bodyType = CommentBodyType.Normal;
            this.commentScoreRank = Comments.CommentScoreRank.None;
            this.isNew = false;
            this.GetApiInfo = null;
            this.score = 0;
        }

        ~NotifyNicoComment()
        {
            GetApiInfo = null;
            if(user != null)this.User.PropertyChanged -= OnPropertyChanged;
        }

        private void getApiInfoRequest()
        {
            if (GetApiInfo != null) GetApiInfo();
        }

        public event Action GetApiInfo;

        internal string BaseBody { get; private set; }

        /// <summary>
        /// コメント番号
        /// </summary>
        public int No
        {
            get
            {
                return this.no;
            }
            set
            {
                if (this.no == value) return;
                this.no = value;
                RaisePropertyChanged("No");
            }
        }

        /// <summary>
        /// コテハン設定済みか判定
        /// </summary>
        public bool HasBeenSetCustomName
        {
            get
            {
                return this.user.HasBeenSetCustomName;
            }
        }

        /// <summary>
        /// コメント投稿したユーザ情報
        /// </summary>
        public NotificationNicoUser User
        {
            get
            {
                return user;
            }

            set
            {
                user = value;
                if (user == null) return;
                user.PropertyChanged += OnPropertyChanged;
            }
        }

        /// <summary>
        /// コメント本文
        /// </summary>
        public NicoCommentBody Body
        {
            get;
            private set;
        }


        /// <summary>
        /// コメントタイプ
        /// </summary>
        public CommentType CommentType
        {
            get;
            private set;
        }


        private static int countBodyLine(NicoCommentBody body)
        {
            return (from bodyText in body.Texts
                    where bodyText.BodyType == NicoCommentBodyTextType.LineBreak
                    select bodyText).Count();
        }

        
        public string GetDisplayBody()
        {

            return this.Body.ToDisplayString();
            
        }
        
        /// <summary>
        /// ユーザアイコンのパス
        /// </summary>
        public string UserIconPath
        {
            get
            {
                if (this.SetRequest)
                {

                    this.getApiInfoRequest();
                }
                return this.User.UserIconPath;
            }
        }

        /// <summary>
        /// ニックネーム
        /// </summary>
        public string NickName
        {
            get
            {
                if (this.SetRequest)
                {
                    this.getApiInfoRequest();
                }
                return this.User.NickName;
            }
        }

        /// <summary>
        /// ユーザID
        /// </summary>
        public string Id
        {
            get
            {
                return this.User.Id;
            }
        }


        /// <summary>
        /// コテハン
        /// </summary>
        public string CustomName
        {
            get
            {
                return this.User.CustomName;
            }
        }

        /// <summary>
        /// 背景色
        /// </summary>
        public Color BackGroundColor
        {
            get
            {
                return this.User.BackGroundColor.Color;
            }
        }

        /// <summary>
        /// 開始時間からの経過時間
        /// </summary>
        public TimeSpan Elapsed
        {
            get
            {
                return this.elapsed;
            }
            set
            {
                if (elapsed == value) return;
                this.elapsed = value;
                RaisePropertyChanged("Elapsed");
            }
        }

        

        /// <summary>
        /// コメント投稿日時
        /// </summary>
        public DateTime Date
        {
            get
            {
                return this.time;
            }
            set
            {
                if (this.time == value) return;
                this.time = value;
                RaisePropertyChanged("Date");
            }
        }

        /// <summary>
        /// 184コメントか判定
        /// </summary>
        public bool IsAnonymous
        {
            get
            {
                return this.User.IsAnonymous;
            }
        }


        /// <summary>
        /// 新規に受信されたコメントか判定
        /// </summary>
        public bool IsNew
        {
            get
            {
                return isNew;
            }
        }

        /// <summary>
        /// ユーザタイプ
        /// </summary>
        public CommentUserType UserType
        {
            get
            {
                return this.userType;
            }
            set
            {

                this.Set(() => this.UserType, ref this.userType, value);
            }
        }


        public CommentBodyType BodyType
        {
            get
            {
                return this.bodyType;
            }

            set
            {
                this.Set(() => this.BodyType, ref this.bodyType, value);
            }
        }


        private int score;

        public int Score
        {
            get
            {
                return this.score;
            }

            set
            {
                this.Set(() => this.Score, ref this.score, value);
            }
        }

        public CommentScoreRank CommentScoreRank
        {
            get
            {
                return this.commentScoreRank;
            }

            set
            {
                this.Set(() => this.CommentScoreRank, ref this.commentScoreRank, value);
            }
        }



        public bool IsJapanese
        {
            get
            {
                return this.isJapanese;
            }
            private set
            {
                this.Set(() => this.IsJapanese, ref this.isJapanese, value);
            }
        }



        public  string GetDisplayName()
        {
            if (this.HasBeenSetCustomName)
            {
                return this.CustomName;
            }
            else if ((!NicoViewApplication.Current.Property.VisibleCustomNameOnly )&& (this.User.NickName != "" || this.SetRequest) )
            {
                return this.NickName;
            }
            else
            {
                return "";
            }
        }

        public bool IsSetApiInfo()
        {
            return  this.User.IsSetApiInfo();
        }

        public bool SetRequest
        {
            get
            {
                return this.User.SetRequest && this.UserType != CommentUserType.Owner && this.UserType != CommentUserType.Manager;
            }

        }

        private void OnPropertyChanged(object sender,PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Id") RaisePropertyChanged(()=>this.IsAnonymous);
            RaisePropertyChanged(e.PropertyName);
        }

        

        internal static NotifyNicoComment Create(NicoLiveChatComment liveComment,NotificationNicoUser user,NicoLiveServiceInfo info,NicoLiveCommentClientInfo commentClientInfo,NicoView.Model.Service.Lives.NicoLiveCommentClient commentClient)
        {
            
            NotifyNicoComment comment = new NotifyNicoComment();
            comment.Elapsed += liveComment.Date - info.StartTime;
            comment.Date = liveComment.Date;
            comment.CommentType = CommentType.Live;

            switch (liveComment.Premium.UserType)
            {
                case NicoChatCommentUserType.General:
                    comment.UserType = CommentUserType.General;
                    break;

                case NicoChatCommentUserType.Premium:
                    comment.UserType = CommentUserType.Premium;
                    break;

                case NicoChatCommentUserType.Owner:
                    comment.UserType = CommentUserType.Owner;
                    break;

                case NicoChatCommentUserType.Alert:
                case NicoChatCommentUserType.Manager:
                    comment.UserType = CommentUserType.Manager;
                    break;
            }

            comment.Body = NicoCommentBody.Parse(comment, liveComment.Body);
            comment.No = liveComment.No;
            
            comment.IsJapanese = liveComment.Locale == "ja-jp" || liveComment.Locale == "jp" || liveComment.Locale == "";
            comment.User = user;
            comment.isNew = comment.Date > commentClientInfo.ThreadConnectionStartTime;
            

            comment.score = liveComment.Score;

            switch (liveComment.CommentScoreRank)
            {
                case NiconicoCommentScoreRank.LittleNg:
                    comment.CommentScoreRank = CommentScoreRank.LittleNg;
                    break;

                case NiconicoCommentScoreRank.MediumNg:
                    comment.CommentScoreRank = CommentScoreRank.MediumNg;
                    break;


                case NiconicoCommentScoreRank.ManyNg:
                    comment.CommentScoreRank = CommentScoreRank.ManyNg;
                    break;

                case NiconicoCommentScoreRank.None:
                    comment.commentScoreRank = CommentScoreRank.None;
                    break;
            }

            s_setAtName(comment);

            comment.BodyType = SetBodyType(comment, liveComment.Body);

            comment.BaseBody = liveComment.Body;

            

            return comment;
        }



        private static bool commentArtCheck(NicoCommentBody body, string text)
        {
            var lineCount = countBodyLine(body);
            var commentArtLength = commentArtCheckRegex.Match(text).Length;
            return  (lineCount >=2  &&(text.Length - commentArtLength <= 0)) || lineCount >= 3;
        }

        private static void s_setAtName(NotifyNicoComment comment)
        {
            if (NicoViewApplication.Current.Property.AtMarkNameToCustomName && comment.UserType != CommentUserType.Owner && comment.UserType != CommentUserType.Manager)
            {
                var lastCommment = comment.Body.Texts.Last();
                if (lastCommment.HasUserName)
                {
                    comment.User.CustomName = lastCommment.GetUserName();
                }
            }
        }

        private static bool _isOwnerShowCommand(NicoCommentBody body)
        {
            return body.IsOwnerShowCommand;
        }

        private static bool _isUserShowCommand(NicoCommentBody body)
        {
            return body.IsUserShowCommand;
        }

        public static CommentBodyType SetBodyType(NotifyNicoComment comment, string body)
        {


            if (comment.Body.IsCommand)
            {

                if (comment.Body.IsNoneShowCommand)
                {
                    return CommentBodyType.NoneShowCommand;
                }

                if ((comment.UserType == CommentUserType.Manager || comment.UserType == CommentUserType.Owner))
                {
                    

                    return _isOwnerShowCommand(comment.Body) ? CommentBodyType.ShowCommand : CommentBodyType.ManageCommand;

                    
                }
                else if (_isUserShowCommand(comment.Body))
                {
                    return CommentBodyType.ShowCommand;

                }



            }

            if (commentArtCheck(comment.Body, body))
            {
                return CommentBodyType.CommentArt;

            }
            else
            {
                return CommentBodyType.Normal;
            }

            

        }
        

        private static readonly Regex commentArtCheckRegex = new Regex(@"[\w]");

        
    }
}
